﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Timers;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using Collab.Services.Remote.Entities;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Editor;
using System.Diagnostics;

namespace Collab.Modules.ViewTracker
{
    public class HighlightAdornment
    {
        private readonly IAdornmentLayer layer;
        public IWpfTextView View { get; set; }
        private List<Highlight> highlights = new List<Highlight>();

        private UserSelection lastSelection;
        private Action<UserSelection> selectionChangedDelegate;
        private Timer updateTimer;

        public HighlightAdornment(IWpfTextView view, Action<UserSelection> selectionChangedDelegate)
        {
            this.View = view;
            this.View.LayoutChanged += OnLayoutChanged;
            this.layer = view.GetAdornmentLayer("HighlightAdornment");
            this.selectionChangedDelegate = selectionChangedDelegate;
            SetupEvents(view);

            //Set up a timer which will handle delaying reparsing if the user is selecting fast
            this.updateTimer = new Timer(200);
            this.updateTimer.AutoReset = false;
            this.updateTimer.Elapsed += (o, e) =>
            {
                ParseSelection();
            };
        }

        private void SetupEvents(IWpfTextView view)
        {
            view.Selection.SelectionChanged += (sender, e) =>
            {
                
                //If the selection changed reset the timer
                this.updateTimer.Stop();
                this.updateTimer.Start();
            };
        }

        private void ParseSelection()
        {
            UserSelection selection = new UserSelection{ 
                CaretPosition = this.View.Selection.Start.Position.Position, 
                CaretLine = this.View.Selection.Start.Position.GetContainingLine().LineNumber 
            };
            try
            {
                if (this.View.Selection.Mode == TextSelectionMode.Box)
                {
                    var selectedSpans = this.View.Selection.SelectedSpans.ToArray();

                    selection.SelectedSpans = selectedSpans.Where(span => !span.IsEmpty).Select(span => new SelectionSpan
                    {
                        Start = span.Start.Position,
                        Length = span.Length,
                    }).ToList();
                }
                else
                {
                    selection.SelectedSpans.Add(new SelectionSpan
                    {
                        Start = this.View.Selection.Start.Position.Position,
                        Length = this.View.Selection.End.Position - this.View.Selection.Start.Position
                    });
                }

                if (this.lastSelection != selection)
                    System.Threading.Tasks.Task.Factory.StartNew(() => selectionChangedDelegate.Invoke(selection));
                lastSelection = selection;
            }
            catch (ArgumentException){}
            catch (InvalidOperationException){}
            
        }

        private void OnLayoutChanged(object sender, TextViewLayoutChangedEventArgs e)
        {
            DrawHighlights();
        }

        public void AddHighlight(ITrackingSpan span, Color color)
        {
            this.highlights.Add(new Highlight() { Color = color, Span = span });
            DrawHighlights();
        }

        public void RemoveHighlights(Color color)
        {

            this.highlights = this.highlights.Where(p => p.Color != color).ToList();
            DrawHighlights();

        }

        private void DrawHighlights()
        {
            Application.Current.Dispatcher.BeginInvoke(new Action(() =>
                {
                    this.layer.RemoveAllAdornments();
                    foreach (var highlight in this.highlights)
                    {
                        try
                        {
                            SnapshotSpan span = highlight.Span.GetSpan(this.View.TextSnapshot);
                            Brush brush = new SolidColorBrush(highlight.Color);
                            Pen pen = new Pen(new SolidColorBrush(Colors.Black), 1);

                            Geometry g = this.View.TextViewLines.GetMarkerGeometry(span);
                            if (g != null)
                            {
                                GeometryDrawing drawing = new GeometryDrawing(brush, pen, g);
                                drawing.Freeze();

                                DrawingImage drawingImage = new DrawingImage(drawing);
                                drawingImage.Freeze();

                                Image image = new Image();
                                image.Source = drawingImage;

                                //Align the image with the top of the bounds of the text geometry
                                Canvas.SetLeft(image, g.Bounds.Left);
                                Canvas.SetTop(image, g.Bounds.Top);

                                this.layer.AddAdornment(AdornmentPositioningBehavior.TextRelative, span, null, image, null);

                            }
                        }
                        catch (ArgumentException)
                        {
                            //Ignore this one
                            Debug.WriteLine("Highlighting failed");
                        }
                        
                    }
                }));

        }
    }
}
